#include "lib.h"

/*
 * create a file /proc/linalter
 * write 'a1' to /proc/linalter: 
 *     the vmas of the current process(altered process) become readable by other processes.
 * write 'a0' to /proc/linalter: 
 *     the vmas of the current process become unreadable by other processes.
 * write 'c%ld %lx %lx %lx %lx' to /proc/linalter: 
 *     means pid, &from, &len, &to, &result
 *     copy a block of memory to specified buffer.
 * write 'w%ld %lx %lx %lx %lx' to /proc/linalter:
 *     means pid, &to, &from, &len, &result
 *     copy a block of memory to specified buffer.
 * read /proc/linalter: 
 *     get '1' means the vmas of the current process is readable, '0' otherwise.
 *     pids of all children of the altered processes follow.
 */

int read_proc_entry(char *page, char **start, off_t offset, int count, int *eof, void *data)
{
	struct task_struct *t;
	int len = 0;
	len += sprintf(page+len, amitraced()?"1\n":"0\n");
	t = tracedtask();
	if(t)
		showpid(t, page, &len);
	else
		PRINTK("<3>linalter: No traced process.\n");
	*eof = 1;
	return len;	
}

int write_proc_entry(struct file *file, const char __user *buff, unsigned long count, void *data)
{
	if(count<100){
		char buf[count+1];
		int y;
		unsigned long from, len, to, result;
		long pid;
		if (copy_from_user(buf, buff, count)==0){
			switch(buf[0]){
				case 'a':
					if(sscanf(buf+1, "%d", &y)==1){
						if(traceme(y)==0)
							return count;
						else
							PRINTK("<3>linalter: cannot trace/untrace.\n");
					}else
						PRINTK("<3>linalter: \"%s\" format not correct, should be 'a1' 'a0'\n", buf);
					break;
				case 'c':
					if(sscanf(buf+1, "%ld %lx %lx %lx %lx", &pid, &from, &len, &to, &result)==5){
						if( access_ok(VERIFY_WRITE, result, sizeof(int)) ){
							*(int *)result = copy( pid, (char *)from, len, (char *)to );
							PRINTK("<3>linalter: copy %ld %lx %lx %lx\n", pid, from, len, to);
							return count;
						}else
							PRINTK("<3>linalter: result(%lx) is not writable.\n", result);
					}else
						PRINTK("<3>linalter: '%s' incorrect format, should be 'c%%ld %%lx %%lx %%lx %%lx'.\n", buf);
					break;
				case 'w':
					if(sscanf(buf+1, "%ld %lx %lx %lx %lx", &pid, &to, &from, &len, &result)==5){
						if( access_ok(VERIFY_WRITE, result, sizeof(int)) ){
							*(int *)result = write_mem( pid, (char *)to, (char *)from, len );
							return count;
						}else
							PRINTK("<3>linalter: result(%lx) is not writable.\n", result);
					}else
						PRINTK("<3>linalter: '%s' incorrect format, should be 'w%%ld %%lx %%lx %%lx %%lx'.\n", buf);
				default:
					PRINTK("<3>linalter: unknown command %c\n", buf[0]);
					break;
			} //end switch
		}else
			PRINTK("<3>linalter: Cannot read buffer.\n");
	}else
		PRINTK("<3>linalter: Too many bytes.\n");
	return -EFAULT;	
}

int __init linalter_init(void)
{
	struct proc_dir_entry *entry;
	entry = create_proc_entry("linalter", 0666, 0); //all readable & writable
	if (entry == 0) {
		PRINTK("<3>linalter: Cannot create /proc/linalter\n");
		goto err1;
	} else {
		entry->owner = THIS_MODULE;
		entry->read_proc = read_proc_entry;
		entry->write_proc = write_proc_entry;
	}
	return 0;
err1:
	return -1;
}

void __exit linalter_exit(void)
{
	remove_proc_entry("linalter", 0);
}

module_init(linalter_init);
module_exit(linalter_exit);
MODULE_AUTHOR("homer.xing@gmail.com");
MODULE_LICENSE("GPL");
